package com.crois.barrier.web.configuration;


import com.crois.barrier.plugin.BarrierPlugin;
import com.crois.barrier.web.config.HttpClientProperties;
import com.crois.barrier.web.plugin.post.NettyClientResponsePlugin;
import com.crois.barrier.web.plugin.post.WebClientResponsePlugin;
import com.crois.barrier.web.plugin.route.NettyHttpClientRoutingPlugin;
import com.crois.barrier.web.plugin.route.WebClientRoutingPlugin;
import io.netty.channel.ChannelOption;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.util.StringUtils;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.netty.http.client.HttpClient;
import reactor.netty.resources.ConnectionProvider;
import reactor.netty.tcp.ProxyProvider;

import java.security.cert.X509Certificate;


/**
 * @author Hou Ze Yu
 * @description http configuration
 * @date 2020/7/13
 */
public class HttpClientConfiguration {

    /***
     * Door http client
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    public HttpClient httpClient(final HttpClientProperties properties){
        /**
         * http pool 配置
         */
        HttpClientProperties.Pool pool = properties.getPool();
        ConnectionProvider connectionProvider;
        if (pool.getType() == HttpClientProperties.Pool.PoolType.DISABLED){
            connectionProvider = ConnectionProvider.newConnection();
        }else if (pool.getType() == HttpClientProperties.Pool.PoolType.FIXED){
            connectionProvider = ConnectionProvider.fixed(pool.getName()
                    ,pool.getMaxConnections()
                    ,pool.getAcquireTimeout());
        }else {
            connectionProvider = ConnectionProvider.elastic(pool.getName());
        }
        /**
         * http客户端配置
         */
        HttpClient httpClient = HttpClient.create(connectionProvider)
                .tcpConfiguration(tcpClient -> {
                    if (properties.getConnectTimeout()!=null){
                       tcpClient = tcpClient.option(
                               ChannelOption.CONNECT_TIMEOUT_MILLIS,
                               properties.getConnectTimeout()
                       );
                    }
                    /**
                     * 如果设置了代理 则 配置代理
                     */
                    HttpClientProperties.Proxy proxy = properties.getProxy();
                    if (StringUtils.hasText(proxy.getHost())){
                            tcpClient  = tcpClient.proxy(proxySpec -> {
                                ProxyProvider.Builder builder = proxySpec
                                        .type(ProxyProvider.Proxy.HTTP)
                                        .host(proxy.getHost());
                                PropertyMapper mapper = PropertyMapper.get();
                                mapper.from(proxy::getPort).whenNonNull().to(builder::port);
                                mapper.from(proxy::getUsername).whenHasText().to(builder::username);
                                mapper.from(proxy::getPassword).whenHasText().to(
                                        password -> builder.password(s->password));
                            });
                    }
                      return tcpClient;
                });
        /**
         * 配置Ssl
         */
        HttpClientProperties.Ssl ssl = properties.getSsl();
         if (ssl.getTrustedX509CertificatesForTrustManager().length>0||ssl.isUseInsecureTrustManager()){
            httpClient = httpClient.secure(sslContextSpec -> {
                // 配置ssl
                SslContextBuilder sslContextBuilder = SslContextBuilder.forClient();
                X509Certificate[] trustedX509Certificates= ssl.getTrustedX509CertificatesForTrustManager();
                if (trustedX509Certificates.length>0){
                   sslContextBuilder.trustManager(trustedX509Certificates);
                }else if (ssl.isUseInsecureTrustManager()){
                   sslContextBuilder.trustManager(InsecureTrustManagerFactory.INSTANCE);
                }
                sslContextSpec.sslContext(sslContextBuilder).defaultConfiguration(ssl.getDefaultConfigurationType())
                        .handshakeTimeout(ssl.getHandshakeTimeout())
                        .closeNotifyFlushTimeout(ssl.getCloseNotifyFlushTimeout())
                        .closeNotifyReadTimeout(ssl.getCloseNotifyReadTimeout());
            });
         }
         if (properties.isWiretap()){
             httpClient = httpClient.wiretap(true);
         }
        return httpClient;
    }
    /**
     * web client 配置
     */
    @ConditionalOnProperty(name = "barrier.httpclient.strategy",havingValue = "webClient")
    @Configuration
    static class WebClientConfiguration{

        /**
         * web client 插件
         * @param httpClient
         * @return
         */
        @Bean
        public BarrierPlugin webClientPlugin(final HttpClient httpClient){
            WebClient webClient = WebClient
                    .builder()
                    .clientConnector(new ReactorClientHttpConnector(httpClient))
                    .build();
            return new WebClientRoutingPlugin(webClient);
        }

        /**
         * webclient 响应插件
         * @return
         */
        @Bean
        public BarrierPlugin WebClientResponsePlugin(){
            return new WebClientResponsePlugin();
        }
    }

    /**
     *netty client 配置
     */
    @ConditionalOnProperty(name = "barrier.httpclient.strategy",havingValue = "netty",matchIfMissing = true)
    @Configuration
    static class NettyHttpclientConfiguration{
        /**
         * netty client 插件
         * @param httpClient
         * @return
         */
        @Bean
        public BarrierPlugin nettyHttpclientPlugin(final HttpClient httpClient){
           return new NettyHttpClientRoutingPlugin(httpClient);
        }
        /**
         * netty 响应插件
         * @return
         */
        @Bean
        public BarrierPlugin nettyClientResponsePlugin(){
            return new NettyClientResponsePlugin();
        }
    }
}
